package cn.ztydata.teachingsystem.heaven.service;

import cn.ztydata.teachingsystem.common.Page;
import cn.ztydata.teachingsystem.exception.ServiceException;
import cn.ztydata.teachingsystem.heaven.common.WebContext;
import cn.ztydata.teachingsystem.heaven.cons.Constants;
import cn.ztydata.teachingsystem.heaven.cons.Roles;
import cn.ztydata.teachingsystem.heaven.dao.*;
import cn.ztydata.teachingsystem.heaven.entity.*;
import cn.ztydata.teachingsystem.heaven.exception.ExcelException;
import cn.ztydata.teachingsystem.util.ExcelUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.*;

/**
 * 教师服务类
 * Created by chenxin on 2014/11/28.
 */
@Service
public class TeacherService extends BaseService {
    @Autowired
    private TeacherDao teacherDao;
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private CourseDao courseDao;
    @Autowired
    private ResourceDao resourceDao;
    @Autowired
    private DepartmentDao departmentDao;
    @Autowired
    private CourseService courseService;
    @Autowired
    private TeacherTeamService teacherTeamService;
    @Autowired
    private ResourceService resourceService;
    @Autowired
    private DepartmentService departmentService;
    @Autowired
    private SpecialtyDao specialtyDao;
    @Autowired
    private CourseGroupDao courseGroupDao;
    @Autowired
    private TeacherTeamDao teacherTeamDao;
    @Autowired
    private WebContext webContext;
    @Autowired
    private HanyuPinyinOutputFormat hanyuPinyinOutputFormat;
    @Autowired
    private SpecialtyGroupService specialtyGroupService;

    //<editor-fold "1--3期">

    /**
     * 读取Excel文件
     * @param in 文件输入流
     *
     * @author chenxin
     * @since 2014/11/28
     */
    public List<Teacher> readFromExcel(InputStream in) {
        //定义标题-属性的映射
        LinkedHashMap<String, String> fieldMap = Maps.newLinkedHashMap();
        fieldMap.put("工号", "number");
        fieldMap.put("姓名", "name");
        fieldMap.put("系部编码", "deptNum");
        fieldMap.put("出生年月", "birthday");
        fieldMap.put("性别", "sex");
        fieldMap.put("学位", "degree");
        fieldMap.put("职称", "title");
        fieldMap.put("学历", "education");

        log.debug("convert records to list");
        return ExcelUtil.excelToList(in, Teacher.class, fieldMap);
    }

    /**
     * 检查导入教师数据中的工号冲突
     * @param teacherList 教师集合
     *
     * @author cx
     * @since 2014-12-04
     */
    public void checkForNum(final List<Teacher> teacherList) {
        if (teacherList.size() > Constants.TOTAL_2K) {
            log.debug("导入数据量超过" + Constants.TOTAL_2K);
            throw new ExcelException("导入数据量超过" + Constants.TOTAL_2K);
        }

        //抽取教师的工号集合
        Set<String> teacherNumSet = Sets.newHashSet();
        int index = 1;
        log.debug("循环导入的教师集合");
        for (Teacher teacher : teacherList) {
            if (teacher.getNumber() == null) {
                log.debug("教师工号 is null");

                throw new ExcelException("导入数据中第" + index + "条数据工号为空");
            }

            //检查导入数据中是否存在重复工号
            if (teacherNumSet.contains(teacher.getNumber())) {
                log.debug("导入数据中的教师工号重复");

                throw new ExcelException("导入数据中第" + index + "条数据的工号重复" );
            }

            teacherNumSet.add(teacher.getNumber());
            index++;

            //转换生成姓名全拼
            teacher.setPinyin(toPinyin(teacher.getName()));
        }

        //检查导入数据工号是否与数据库中记录冲突
        String duplicateNum = teacherDao.getDuplicateNum(teacherNumSet);
        teacherNumSet.clear();
        if (duplicateNum != null) {
            log.debug("数据库中存在导入的教师工号");

            throw new ExcelException("工号为:" + duplicateNum + "的教师已经存在");
        }
    }

    /**
     * 生成拼音
     * @param str 传入字符串
     * @return 拼音
     *
     * @author fanruiping
     * @since 2015-05-08
     */
    private String toPinyin(String str){
        char[] chars = str.toCharArray();
        StringBuilder pinyin = new StringBuilder();
        try {
            for (char c : chars) {
                String[] pinyinStr = PinyinHelper.toHanyuPinyinStringArray(c, hanyuPinyinOutputFormat);
                if (pinyinStr != null){
                    pinyin.append(pinyinStr[0]);
                }
                else {
                    pinyin.append(c);
                }
            }
        } catch (BadHanyuPinyinOutputFormatCombination ex){
            log.debug("转换姓名全拼时发生异常");
            log.warn("转换姓名全拼时发生异常:name->{}", str);
            throw new ServiceException("转换姓名全拼时发生异常:" + str);
        }
        return pinyin.toString().toUpperCase();
    }

    /**
     * 批量新增教师
     * @param teacherList 教师集合
     *
     * @author cx
     * @since 2014-11-29
     */
    @Transactional
    public void saveTeachers(final List<Teacher> teacherList) {
        try {
            //执行批量插入
            teacherDao.saveTeachers(teacherList);
        } catch (DataAccessException ex) {
            log.debug("导入教师-批量新增异常");

            log.warn("导入教师-批量新增异常", ex);
            throw new ExcelException();
        }
    }

    /**
     * 获取教师明细
     * @param id 编号
     * @return Teacher
     *
     * @author fanruiping
     * @since 2014-11-29
     */
    public Teacher getDetail(Long id){
        log.debug("获取教师明细");
        return teacherDao.getById(id);
    }

    /**
     * 更新教师数据
     * @param entity 实体类
     *
     * @author fanruiping
     * @since 2014-11-29
     */
    public void update(Teacher entity){
        //更新课程创建者名称
        if(-1 == courseDao.updateCreatorName(entity.getId(),entity.getName())){
            log.debug("update course creatorName fail");

            log.warn("更新课程创建者名称失败");

            throw new ServiceException("更新课程创建者名称失败");
        }

        //更新资源上传者名称
        if(-1 == resourceDao.updateUploaderName(entity.getId(),entity.getName())){
            log.debug("update resource uploaderName fail");

            log.warn("更新资源上传者名称失败");

            throw new ServiceException("更新资源上传者名称失败");
        }

        if(!teacherDao.modify(entity)){
            log.debug("update teacher detail fail");

            log.error("更新教师信息失败");

            throw new ServiceException("更新教师信息失败");
        }
    }

    /**
     * 验证系部下是否有教师数据
     * @param deptNum 系部编码
     * @return Boolean
     *
     * @author fanruiping
     * @since 2014-11-29
     */
    public Boolean checkHasTeacherByDeptNum(String deptNum){
        log.debug("判断系部下是否有教师数据");

        return teacherDao.checkByDeptNum(deptNum);
    }

    /**
     * 验证用户登录信息
     * @param entity
     * @return User
     *
     * @author fanruiping
     * @since 2014-12-03
     */
    public Teacher checkUserLogin(User entity){
        Teacher teacher = teacherDao.checkNumberAndPassword(entity);

        //验证用户登录信息是否正确
        if(null == teacher){
            log.debug("user is not exist:null==teacher");

            return null;
        }

        //验证用户是否拥有指定角色(除教师外)
        if(Roles.TEACHER != entity.getRole() && !roleDao.checkRole(teacher.getId(), entity.getRole())){
            log.debug("The user does not specify roles:Roles.TEACHER!=entity.getRole()&& !roleDao.checkRole(teacher.getId(),entity.getRole())");

            return null;
        }

        Department department = departmentService.getDetailByNumber(teacher.getDeptNum());

        //判断是否获取到部门信息
        if(null != department){
            log.debug("the department is not exist:null!=department");

            teacher.setDeptName(department.getName());
        }

        log.debug("user is exist:null!=teacher");

        return teacher;
    }

    /**
     * 统计教师的信息
     * @param deptNum 系部编码
     * @param keyword 搜索关键字
     * @return List
     *
     * @author cx
     * @since 2014-12-03
     */
    @Cacheable(value = "teachingSystem:cache:count", key = "'teacher:'.concat(#deptNum).concat(#keyword == null ? '' : #keyword)")
    public List<TeacherStatistics> getStatisticsInfo(final String deptNum, final String keyword) {
        //获取教师的基本信息
        List<TeacherStatistics> teacherInfoList = teacherDao.getListForCount(deptNum, keyword);
        if (teacherInfoList.isEmpty()) {
            log.debug("暂无教师统计信息:teacherInfoList.isEmpty()");
            throw new ServiceException("暂无教师统计信息");
        }

        //抽取教师的ID集合
        List<Long> teacherIdList = Lists.newArrayList();
        log.debug("循环教师ID集合");
        for (TeacherStatistics teacherStatistics : teacherInfoList) {
            teacherIdList.add(teacherStatistics.getId());
        }

        //获取教师的建课总数
        Map<Long, Long> buildCourse = courseService.getCourseTotalForTeacher(teacherIdList);
        //获取教师的任课总数
        Map<Long, Long> teachingCourse = teacherTeamService.getCourseTotal(teacherIdList);
        //获取教师上传的资源数
        Map<Long, Long> teacherToRsc = resourceService.countForTeacher(teacherIdList);

        log.debug("循环教师统计信息");
        //组合系部资源的统计信息
        for (TeacherStatistics teacherStatistics : teacherInfoList) {
            //获取建课数量、任课数量、资源数量
            Long buildCourseTotal = buildCourse.get(teacherStatistics.getId());
            Long teachingCourseTotal = teachingCourse.get(teacherStatistics.getId());
            Long rscTotal = teacherToRsc.get(teacherStatistics.getId());

            teacherStatistics.setBuildCourseTotal(buildCourseTotal == null ? 0 : buildCourseTotal);
            teacherStatistics.setTeachCourseTotal(teachingCourseTotal == null ? 0 : teachingCourseTotal);
            teacherStatistics.setRscTotal(rscTotal == null ? 0 : rscTotal);
        }

        log.debug("return 教师统计信息");
        return teacherInfoList;
    }

    /**
     * 教师列表
     * @param keyword 关键字
     * @param deptNum 系部编码
     * @param pageNum 页码
     * @param pageSize 每页显示条数
     * @return Page
     *
     * @author fanruiping
     * @since 2014-12-11
     */
    public Page<Teacher> getList(String keyword, String deptNum, int pageNum, int pageSize){
        Page<Teacher> page = teacherDao.getList(keyword, deptNum, pageNum, pageSize);

        List<String> deptNums = new ArrayList<>();

        log.debug("遍历教师列表：for(Teacher entity : page.getItems())");
        for(Teacher entity : page.getItems()){
            deptNums.add(entity.getDeptNum());
        }

        if(!deptNums.isEmpty()){
            log.debug("如果系部编码不为空：!deptNums.isEmpty()");
            Map<String,String> departments = departmentDao.getNamesByNumbers(deptNums);

            log.debug("遍历教师列表1：for(Teacher entity : page.getItems())");
            for(Teacher entity : page.getItems()){
                entity.setDeptName(departments.get(entity.getDeptNum()));
            }
        }

        log.debug("教师列表");
        return page;
    }

    /**
     * 获取教师团队信息
     * @param teacherIds 教师Id集合
     * @return Map
     *
     * @author cx
     * @since 2014-12-13
     */
    public Map<Long, Teacher> getAllForTeam(Set<Long> teacherIds) {
        Map<Long, Teacher> result = Maps.newHashMap();
        if (teacherIds == null || teacherIds.isEmpty()) {
            log.debug("teacherIds is empty:teacherIds == null || teacherIds.isEmpty()");
            return result;
        }

        //获取教师信息
        List<Teacher> teachers = teacherDao.getAll(teacherIds);
        log.debug("循环转换团队教师信息格式");
        //将结果转换为“教师ID->教师”的格式
        for (Teacher teacher : teachers) {
            result.put(teacher.getId(), teacher);
        }

        log.debug("return 团队教师信息map");
        return result;
    }

    /**
     * 获取教师的姓名与头像
     * @param teacherIds 教师ID集合
     * @return Map
     *
     * @author cx
     * @since 2014-12-15
     */
    public Map<Long, Teacher> getPictures(Set<Long> teacherIds) {
        Map<Long, Teacher> result = Maps.newHashMap();
        if (teacherIds == null || teacherIds.isEmpty()) {
            log.debug("教师ID集合 is empty:teacherIds == null || teacherIds.isEmpty()");

            return result;
        }

        //获取教师信息
        List<Teacher> teachers = teacherDao.getAllForPic(teacherIds);

        log.debug("循环转换团队教师头像格式");

        //将结果转换为“教师ID->教师”的格式
        for (Teacher teacher : teachers) {
            result.put(teacher.getId(), teacher);
        }

        log.debug("return 团队教师头像map");

        return result;
    }

    /**
     * 获取教师列表（教师团队选人）
     * @param pageNum 页码
     * @param pageSize 每页条数
     * @param deptNum 系部编码
     * @param keyword 搜索关键字
     * @return Page
     *
     * @author cx
     * @since 2014-12-15
     */
    public Page<Teacher> getListForTeam(final int pageNum, final int pageSize, final String deptNum, String keyword) {
        //拆分搜索关键字
        String[] keywords = StringUtils.split(keyword);

        Page<Teacher> teacherPage = teacherDao.getAllForTeam(pageNum, pageSize, deptNum, keywords);
        if (teacherPage.getItems().size() > 0) {
            log.debug("教师分页集合不为空:teacherPage.getItems().size() > 0");
            //获取教师所属的系部编码集合
            List<String> deptNumList = Lists.newArrayList();
            log.debug("循环生成教师所属系部编码集合");
            for (Teacher teacher : teacherPage.getItems()) {
                deptNumList.add(teacher.getDeptNum());
            }

            //获取教师所属系部的名称
            Map<String, String> deptMap = departmentDao.getNamesByNumbers(deptNumList);
            log.debug("循环绑定系部名称");
            for (Teacher teacher : teacherPage.getItems()) {
                teacher.setDeptName(deptMap.get(teacher.getDeptNum()));
            }
        }

        log.debug("return 教师分页集合");
        return teacherPage;
    }

    /**
     * 修改密码
     * @param password 密码
     * @param userId 用户编号
     * @return Boolean
     *
     * @author fanruiping
     * @since 2014-12-29
     */
    public Boolean changePassword(String password, Long userId){
        if(teacherDao.updatePassword(password,userId) > 0){
            log.debug("更新密码成功:return true");

            return true;
        }

        log.debug("更新密码失败:return false");

        return false;
    }

    /**
     * 验证用户密码
     * @param password 密码
     * @param teacherId 用户编号
     * @return Boolean
     *
     * @author fanruiping
     * @since 2014-12-29
     */
    public Boolean checkUserPassword(String password,Long teacherId){
        log.debug("验证用户密码");

        return teacherDao.checkUserPassword(teacherId,password);
    }

    /**
     * 获取首页系部教师列表
     *
     * @author chenxin
     * @since 2015/1/11
     */
    public List<Teacher> getIndexList(final String deptNum, final int top) {
        //获取指定系部下的n个教师信息
        log.debug("获取指定系部下的n个教师信息");
        return teacherDao.getListForIndex(deptNum, top);
    }

    /**
     * 获得个人明细
     * @param id id
     * @return Json
     *
     * @author wyh
     * @since 2015-03-31
     */
    public Teacher getInfo(long id){
        Teacher teacher = teacherDao.getById(id);
        if(teacher != null) {
            log.debug("教师信息不为空：teacher != null");
            Department department = departmentService.getDetailByNumber(teacher.getDeptNum());
            if(department != null) {
                log.debug("系部信息不为空：department != null");
                teacher.setDeptName(department.getName());
            }
        } else {
            log.debug("教师信息为空：else");
            throw new ServiceException("查询记录不存在");
        }

        log.debug("返回 教师个人明细");
        return teacher;
    }

    //</editor-fold>


    //<editor-fold "专业群组">

    /**
     * 跟专业ID分页获取其教师成员
     *
     * @param specId 专业ID
     * @param letter 姓名首字母
     * @param pageNum 当前页码
     * @param pageSize 每页条数
     * @param search 关键字
     * @return Page<Teacher>
     * @author lyj
     * @since 2015-5-6
     */
    public Page<Teacher> getSpecialtyTeachers(final long specId, final String letter, final int pageNum, final int pageSize, final String search, final User userInfo) {

        if (webContext.getRole() != Roles.SPEC_LEADER && webContext.getRole() != Roles.TEACHER) {
            log.debug("登录用户没有权限使用该接口webContext.getRole() != Roles.SPEC_LEADER && webContext.getRole() != Roles.TEACHER");

            log.warn("登录用户没有权限使用该接口！登录用户角色->{}", webContext.getRole());
            throw new ServiceException("权限错误！");
        }

        //判断登录用户是否可以获取该专业群组信息
        if (!specialtyGroupService.getSpecialtyIds(userInfo).contains(specId)) {
            throw new ServiceException("权限错误！");
        }

        //存储所有的教师ID
        Set<Long> teacherIds = new HashSet<>();

        //根据专业ID获取该专业负责ID
        Specialty specialty = specialtyDao.getById(specId);
        if (specialty == null) {
            log.debug("根据专业ID获取专业信息失败specialty == null");

            log.warn("根据专业ID获取专业信息失败！specialtyId->{}", specId);
            throw new ServiceException("获取教师成员失败！");
        }
        //将专业负责人ID添加到教师ID集合
        teacherIds.add(specialty.getLeaderId());


        //根据专业ID获取该专业下的所有课程ID集合、课程代码集合
        List<Course> courseList = courseDao.getIdsBySpecId(specId);

        if (courseList.size() != 0) {

            log.debug("courseList.size() != 0");

            Set<String> courseCodes = new HashSet<>(); //存储课程代码集合
            Set<Long> courseIds = new HashSet<>(); //存储课程Id集合

            log.debug("Course course : courseList");
            for (Course course : courseList) {
                courseCodes.add(course.getLessonCode());
                courseIds.add(course.getId());
            }


            //根据课程代码集合获取该课程下的所有课程群组的负责人ID
            teacherIds.addAll(courseGroupDao.getTeacherIdsByCourseCode(courseCodes));

            //根据课程ID集合获取每个课程教师团队成员的ID
            teacherIds.addAll(teacherTeamDao.getTeacherIdsByCourseId(courseIds));

        }

        if (teacherIds.size() == 0) {
            log.debug("teacherIds.size() == 0");
            return new Page<>();
        }

        log.debug("跟专业ID分页获取其教师成员");
        return teacherDao.getTeachersByIds(teacherIds, letter, pageNum, pageSize, search);

    }


    /**
     * 获取活跃教师
     *
     * @param specId 专业ID
     * @param count  条数
     * @return List<Teacher>
     * @author lyj
     * @since 2015-5-6
     */
    public List<Teacher> getHotTeachers(final long specId, final int count, final User userInfo) {

        if (webContext.getRole() != Roles.SPEC_LEADER && webContext.getRole() != Roles.TEACHER) {
            log.debug("登录用户没有权限使用该接口webContext.getRole() != Roles.SPEC_LEADER && webContext.getRole() != Roles.TEACHER");

            log.warn("登录用户没有权限使用该接口！登录用户角色->{}", webContext.getRole());
            throw new ServiceException("权限错误！");
        }

        //判断登录用户是否可以获取该专业群组信息
        if (!specialtyGroupService.getSpecialtyIds(userInfo).contains(specId)) {
            throw new ServiceException("权限错误！");
        }

        //存储所有的教师ID
        Set<Long> teacherIds = new HashSet<>();

        //根据专业ID获取该专业负责ID
        Specialty specialty = specialtyDao.getById(specId);
        if (specialty == null) {
            log.debug("specialty == null");

            log.warn("根据专业ID获取专业信息失败！specialtyId->{}", specId);
            throw new ServiceException("获取教师成员失败！");
        }
        //将专业负责人ID添加到教师ID集合
        teacherIds.add(specialty.getLeaderId());


        //根据专业ID获取该专业下的所有课程ID集合、课程代码集合
        List<Course> courseList = courseDao.getIdsBySpecId(specId);

        if (courseList.size() != 0) {
            log.debug("courseList.size() != 0");

            Set<String> courseCodes = new HashSet<>(); //存储课程代码集合
            Set<Long> courseIds = new HashSet<>(); //存储课程Id集合

            log.debug("循环课程ID集合");
            for (Course course : courseList) {
                courseCodes.add(course.getLessonCode());
                courseIds.add(course.getId());
            }


            //根据课程代码集合获取该课程下的所有课程群组的负责人ID
            teacherIds.addAll(courseGroupDao.getTeacherIdsByCourseCode(courseCodes));

            //根据课程ID集合获取每个课程教师团队成员的ID
            teacherIds.addAll(teacherTeamDao.getTeacherIdsByCourseId(courseIds));
        }

        //如果教师ID集合为空
        if (teacherIds.size() == 0) {
            log.debug("如果教师ID集合为空teacherIds.size() == 0");
            return new ArrayList<>();
        }


        List<Teacher> teacherList = new ArrayList<>();
        //循环获取老师活跃度
        log.debug("循环获取老师活跃度");
        for (Long teacherId : teacherIds) {

            Teacher teacher = new Teacher();
            teacher.setId(teacherId);
            teacher.setActive(teacherDao.getClassCountAll(specId, teacherId));

            teacherList.add(teacher);
        }

        //根据活跃度排序
        log.debug("根据活跃度排序");
        Collections.sort(teacherList, new Comparator<Teacher>() {
            @Override
            public int compare(Teacher o1, Teacher o2) {
                //控制升序、降序排序
                return o2.getActive().compareTo(o1.getActive());
            }
        });


        //如果获取数据条数小于集合内数据总条数
        if (count < teacherList.size()) {
            log.debug("如果获取数据条数小于集合内数据总条数count < studentList.size()");
            teacherList.subList(0, count);
        }

        teacherIds.clear();
        //获取排序后活跃度前几名的教师ID集合
        log.debug("获取排序后活跃度前几名的教师ID集合");
        for (Teacher teacher : teacherList) {
            teacherIds.add(teacher.getId());
        }

        //根据教师Id获取教师信息
        log.debug("获取活跃教师");
        return teacherDao.getTeachersByIds(teacherIds);

    }

    //</editor-fold>

}
